/*
 * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.palantir.atlasdb.timelock.paxos;

import com.codahale.metrics.MetricRegistry;
import com.palantir.atlasdb.AtlasDbMetricNames;
import com.palantir.atlasdb.util.AtlasDbMetrics;
import com.palantir.atlasdb.util.MetricsManager;
import com.palantir.atlasdb.util.MetricsManagers;
import com.palantir.paxos.Client;
import com.palantir.tritium.metrics.registry.SlidingWindowTaggedMetricRegistry;
import com.palantir.tritium.metrics.registry.TaggedMetricRegistry;
import java.util.concurrent.TimeUnit;
import org.immutables.value.Value;

@Value.Immutable
public abstract class TimelockPaxosMetrics {

    abstract PaxosUseCase paxosUseCase();

    abstract MetricRegistry legacyMetrics();

    @Value.Derived
    TaggedMetricRegistry metrics() {
        return new SlidingWindowTaggedMetricRegistry(35, TimeUnit.SECONDS);
    }

    @Value.Derived
    ClientScopedMetrics clientScopedMetrics() {
        return new ClientScopedMetrics(metrics());
    }

    @Value.Derived
    MetricsManager asMetricsManager() {
        return MetricsManagers.of(legacyMetrics(), metrics());
    }

    public static TimelockPaxosMetrics of(PaxosUseCase paxosUseCase, MetricsManager metricsManager) {
        TimelockPaxosMetrics metrics = ImmutableTimelockPaxosMetrics.builder()
                .legacyMetrics(metricsManager.getRegistry())
                .paxosUseCase(paxosUseCase)
                .build();
        metrics.attachToParentMetricRegistry(metricsManager.getTaggedRegistry());
        return metrics;
    }

    public <T, U extends T> T instrument(Class<T> clazz, U instance) {
        return AtlasDbMetrics.instrumentWithTaggedMetrics(metrics(), clazz, instance);
    }

    public <T, U extends T> T instrument(Class<T> clazz, U instance, Client client) {
        return AtlasDbMetrics.instrumentWithTaggedMetrics(
                clientScopedMetrics().metricRegistryForClient(client), clazz, instance);
    }

    private void attachToParentMetricRegistry(TaggedMetricRegistry parent) {
        parent.addMetrics(AtlasDbMetricNames.TAG_PAXOS_USE_CASE, paxosUseCase().toString(), metrics());
    }
}
